import Blocks from "@/components/blocks";
import { fetchSanityPageBySlug, PAGES_SLUGS_QUERY } from "@/sanity/lib/fetch";
import { notFound, permanentRedirect } from "next/navigation";
import { generatePageMetadata } from "@/sanity/lib/metadata";
import {
getDynamicFetchOptions,
sanityFetchMetadata,
sanityFetchStaticParams,
type DynamicFetchOptions,
} from "@/sanity/lib/live";
import { PAGE_QUERY_RESULT, PAGES_SLUGS_QUERY_RESULT } from "@/sanity.types";
import { PAGE_QUERY } from "@/sanity/queries/page";
import { groq } from "next-sanity";
import { draftMode } from "next/headers";
import { Suspense } from "react";
function PageFallback() {
return (
);
}
const RESERVED_STATIC_SLUGS = new Set([
"index",
"about",
"services",
"contact",
"partnership",
"blog",
]);
const KNOWN_POST_SLUGS = new Set([
"vietnam-wedding-guide-part-1",
"why-vietnam",
"wedding-framework-project-managment",
"ngan-sach-tiec-cuoi-cao-cap-viet-nam-phan-1",
"ngan-sach-tiec-cuoi-cao-cap-viet-nam-phan-2",
"tai-sao-chon-viet-nam",
"chn-la-wedding-planner-ch-c-ph-hp-khng-c-tt-nht",
"wedding-in-vietnam-questions",
]);
export async function generateStaticParams() {
const { data: pages } = (await sanityFetchStaticParams({
query: PAGES_SLUGS_QUERY,
})) as { data: PAGES_SLUGS_QUERY_RESULT };
const slugs = (pages ?? []).flatMap((page) => {
const slug = page.slug?.current;
if (!slug) {
return [];
}
const pathParts = slug.split("/").filter(Boolean);
// Exclude dedicated static routes to eliminate routing collisions
if (pathParts.length === 1 && RESERVED_STATIC_SLUGS.has(pathParts[0])) {
return [];
}
return [{ slug: pathParts }];
});
// Next.js 16 Cache Components requires generateStaticParams to return at least 1 item.
// Use a non-colliding placeholder slug so it never conflicts with real static routes like /about.
if (slugs.length === 0) {
return [{ slug: ["_cms-preview"] }];
}
return slugs;
}
export async function generateMetadata(props: {
params: Promise<{ slug: string[] }>;
}) {
const [{ slug }, { perspective }] = await Promise.all([
props.params,
getDynamicFetchOptions(),
]);
const slugPath = slug.join("/");
const { data: page } = (await sanityFetchMetadata({
query: PAGE_QUERY,
params: { slug: slugPath },
perspective,
})) as { data: PAGE_QUERY_RESULT };
if (!page) {
if (slug.length === 1) {
const singleSlug = slug[0];
if (KNOWN_POST_SLUGS.has(singleSlug)) {
permanentRedirect(`/blog/${singleSlug}`);
}
try {
const { data: hasPost } = (await sanityFetchMetadata({
query: groq`count(*[_type == "post" && slug.current == $slug]) > 0`,
params: { slug: singleSlug },
perspective,
})) as { data: boolean };
if (hasPost) {
permanentRedirect(`/blog/${singleSlug}`);
}
} catch (e) {
// ignore
}
}
notFound();
}
return generatePageMetadata({ page, slug: slugPath });
}
export default async function Page(props: {
params: Promise<{ slug: string[] }>;
}) {
const { isEnabled: isDraftMode } = await draftMode();
if (isDraftMode) {
return (
}>
);
}
const { slug } = await props.params;
return (
);
}
async function DynamicPage({
params,
}: {
params: Promise<{ slug: string[] }>;
}) {
const [{ slug }, { perspective, stega }] = await Promise.all([
params,
getDynamicFetchOptions(),
]);
return (
);
}
async function CachedPage({
slug,
perspective,
stega,
}: { slug: string } & DynamicFetchOptions) {
const page = await fetchSanityPageBySlug({ slug, perspective, stega });
if (!page) {
const pathParts = slug.split("/").filter(Boolean);
if (pathParts.length === 1) {
const singleSlug = pathParts[0];
if (KNOWN_POST_SLUGS.has(singleSlug)) {
permanentRedirect(`/blog/${singleSlug}`);
}
try {
const { data: hasPost } = (await sanityFetchMetadata({
query: groq`count(*[_type == "post" && slug.current == $slug]) > 0`,
params: { slug: singleSlug },
perspective,
})) as { data: boolean };
if (hasPost) {
permanentRedirect(`/blog/${singleSlug}`);
}
} catch (e) {
// ignore
}
}
notFound();
}
return (
);
}